package net.oschina.arvin.sqlbuilder;

import net.oschina.arvin.sqlbuilder.definition.ColumnDefinition;
import net.oschina.arvin.sqlbuilder.utils.AssertUtil;
import net.oschina.arvin.sqlbuilder.utils.ReflectUtil;
import net.oschina.arvin.sqlbuilder.utils.Util;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Arvin
 * @time 2017/11/16 16:26
 */
public class UpdateBuilder extends AbstractNotQueryBuilder<UpdateBuilder> {


    /** 要更新的对象 */
    private Object model;

    /** Where 语句构造器 */
    private WhereBuilder whereBuilder;

    /** 更新的数目, -1 表示全部更新 */
    private int updateLimitCount = -1;

    /** 是否允许where子句为空 */
    private boolean allowNullWhereCondition = false;

    /** 自定义要更新的列名称集合， 如果没有设置的话就按照默认规则进行计算 */
    private Set<ColumnDefinition> customNeedUpdateColumnDefinitionSet = new HashSet<ColumnDefinition>();

    /**
     * @param model            要更新的对象
     * @param updateLimitCount 要更新的数量，如果要限制所有请传一个小于 0 的数字， 如 -1
     */
    public UpdateBuilder(Object model, int updateLimitCount) {
        super(model.getClass(), SqlBuilderType.UPDATE);
        this.setUpdateLimitCount(updateLimitCount);
        this.model = model;
    }

    /**
     * @param whereBuilder     where 条件
     * @param updateLimitCount 要更新的数量，如果要限制所有请传一个小于 0 的数字， 如 -1
     * @param model            要更新的对象
     */
    public UpdateBuilder(WhereBuilder whereBuilder, int updateLimitCount, Object model) {
        super(model.getClass(), SqlBuilderType.UPDATE);

        this.model = model;
        this.whereBuilder = whereBuilder;
        this.setUpdateLimitCount(updateLimitCount);
    }

    public WhereBuilder getWhereBuilder() {
        return whereBuilder;
    }

    public UpdateBuilder setWhereBuilder(WhereBuilder whereBuilder) {
        this.whereBuilder = whereBuilder;
        return this;
    }

    public UpdateBuilder setUpdateLimitCount(int updateLimitCount) {
        AssertUtil.assertTrue(updateLimitCount != 0, "要更新的数量不能为0");
        this.updateLimitCount = updateLimitCount < 0 ? -1 : updateLimitCount;
        return this;
    }

    public int getUpdateLimitCount() {
        return updateLimitCount;
    }

    public boolean isAllowNullWhereCondition() {
        return allowNullWhereCondition;
    }

    public UpdateBuilder setAllowNullWhereCondition(boolean allowNullWhereCondition) {
        this.allowNullWhereCondition = allowNullWhereCondition;
        return this;
    }

    @Override
    public String getSql() {
        String sql = super.getSql();
        logUsedSql(sql);
        return sql;
    }

    /**
     * <pre>
     * 规则：
     *  1. 如果调用的时候，指定了用于更新的主键，则使用自定义的
     *  2. 先寻找主键， 如果主键不为空就按照主键进行更新
     *  3. 寻找唯一能够标识一行数据的字段，即 UniqueKey 的字段，注意只能有一个
     * </pre>
     *
     * @return
     */
    @Override
    protected String build() {

        StringBuilder sqlBuilder = new StringBuilder("UPDATE ").append(getWrapTablename()).append(" SET ");

        List<ColumnDefinition> needUpdateColumnDefinitions = calculateNeedUpdateColumnDefinitions();

        // 添加要更新的数据库列
        appendUpdateColumns(sqlBuilder, needUpdateColumnDefinitions);

        // 添加 WhereBuilder 构造的条件
        boolean isWhereBuilderAppend = lookupAndAppendWhereBuilderCondition(sqlBuilder);

        if (isWhereBuilderAppend) {
            lookupAndAppendUpdateLimitCount(sqlBuilder);
            return sqlBuilder.toString();
        }

        boolean isPrimaryKeyAppendToWhere = lookupAndAppendPrimaryKeyToWhereClause(sqlBuilder);

        if (isPrimaryKeyAppendToWhere) {
            lookupAndAppendUpdateLimitCount(sqlBuilder);
            return sqlBuilder.toString();
        }

        boolean isUniqueKeyAppendToWhere = lookupAndAppendUniqueKeyToWhereClause(sqlBuilder);

        if (isUniqueKeyAppendToWhere) {
            lookupAndAppendUpdateLimitCount(sqlBuilder);
            return sqlBuilder.toString();
        }

        AssertUtil.assertTrue(isAllowNullWhereCondition(),
                "你的更新语句没有设置where子句 太危险了，如果确实需要更新所有符合条件的请调用: UpdateBuilder.setAllowNullWhereCondition(true): \nsql=" + sqlBuilder.toString());

        lookupAndAppendUpdateLimitCount(sqlBuilder);

        return sqlBuilder.toString();
    }

    /**
     * <pre>
     * 计算需要更新的字段属性列表, 计算规则为：
     *
     * 1. 如果用户自定义了，直接使用自定义的
     * 2. 用户没有自定义，所有值不允许为空的， 但是model中为空的将会被忽略
     * </pre>
     */
    private List<ColumnDefinition> calculateNeedUpdateColumnDefinitions() {

        List<ColumnDefinition> tempList = new ArrayList<ColumnDefinition>();

        if (!this.customNeedUpdateColumnDefinitionSet.isEmpty()) {
            tempList.addAll(this.customNeedUpdateColumnDefinitionSet);
        } else {
            // 没有自定义，计算
            tempList.addAll(getColumnDefinitionList());
        }

        // 清理掉不允许NULL但是目前的值是NULL的列
        List<ColumnDefinition> resultList = new ArrayList<ColumnDefinition>();

        for (ColumnDefinition columnDefinition : tempList) {

            if (columnDefinition.isUpdateIgnore()) {
                continue;
            }

            Object value = ReflectUtil.getFieldValue(this.model, columnDefinition.getField());
            if (null != value) {
                if (columnDefinition.isPrimaryKey()) {
                    if (Util.isValidPrimaryKeyValue(value)) {
                        resultList.add(columnDefinition);
                    }
                } else {
                    resultList.add(columnDefinition);
                }
            } else {
                if (columnDefinition.isUpdateIncludeNull()) {
                    columnDefinition.isUpdateIncludeNull();
                }
            }
        }

        return resultList;
    }

    private boolean lookupAndAppendUniqueKeyToWhereClause(StringBuilder sqlBuilder) {
        // 检查是否有唯一键的值不为空，如有有的话就根据唯一键进行更新
        List<ColumnDefinition> uniqueKeyColumnDefinitions = getUniqueKeyColumnDefinitions();
        if (null != uniqueKeyColumnDefinitions && !uniqueKeyColumnDefinitions.isEmpty()) {
            for (ColumnDefinition uniqueKeyColumnDefinition : uniqueKeyColumnDefinitions) {
                Object value = ReflectUtil.getFieldValue(this.model, uniqueKeyColumnDefinition.getField());
                if (null != value && Util.isNotBlank(String.valueOf(value))) {
                    sqlBuilder.append(" WHERE ").append(uniqueKeyColumnDefinition.getWrapColumnName()).append("=?");
                    addParamByColumnDefinition(uniqueKeyColumnDefinition, value);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 搜索主键， 如果不为空的话就按照主键进行更行
     *
     * @param sqlBuilder sql语句构造器
     * @return
     */
    private boolean lookupAndAppendPrimaryKeyToWhereClause(StringBuilder sqlBuilder) {

        List<ColumnDefinition> primaryKeyColumnDefinitions = getPrimaryKeyColumnDefinitions();
        List<SqlParam> sqlParamList = new ArrayList<SqlParam>();
        if (null != primaryKeyColumnDefinitions && !primaryKeyColumnDefinitions.isEmpty()) {
            StringBuilder primaryKeyWhereBuilder = new StringBuilder(" WHERE ");
            List<Object> primaryKeyValues = new ArrayList<Object>();
            for (ColumnDefinition primaryKeyColumnDefinition : primaryKeyColumnDefinitions) {
                Object value = ReflectUtil.getFieldValue(this.model, primaryKeyColumnDefinition.getField());
                if (Util.isValidPrimaryKeyValue(value)) {
                    primaryKeyWhereBuilder.append(primaryKeyColumnDefinition.getWrapColumnName()).append("=? AND");
                    primaryKeyValues.add(value);
                    sqlParamList.add(new SqlParam(primaryKeyColumnDefinition, value));
                } else {
                    break;
                }
            }
            // 长度相等才能更新
            if (primaryKeyValues.size() == primaryKeyColumnDefinitions.size()) {
                primaryKeyWhereBuilder.setLength(primaryKeyWhereBuilder.length() - 3);
                sqlBuilder.append(primaryKeyWhereBuilder.toString());
                addListSqlParams(sqlParamList);
                return true;
            }
        }

        return false;
    }

    private boolean lookupAndAppendUpdateLimitCount(StringBuilder sqlBuilder) {
        // 添加Limit
        if (updateLimitCount > 0) {
            sqlBuilder.append(" LIMIT ").append(updateLimitCount);
            return true;
        }
        return false;
    }

    /**
     * 如果有设置WhereBuilder 就添加过去，并返回 true 表示添加了， 否则返回 false
     *
     * @param sqlBuilder sql 语句构造器
     * @return
     */
    private boolean lookupAndAppendWhereBuilderCondition(StringBuilder sqlBuilder) {
        if (this.whereBuilder != null && this.whereBuilder.conditionCount() > 0) {
            sqlBuilder.append(" ").append(whereBuilder.getSql());
            addListSqlParams(whereBuilder.getSqlParamList());
            return true;
        }
        return false;
    }

    private void appendUpdateColumns(StringBuilder sqlBuilder, List<ColumnDefinition> needUpdateColumnDefinitions) {
        AssertUtil.assertNotEmpty(needUpdateColumnDefinitions, "没有检测到需要更新的字段！");
        for (ColumnDefinition columnDefinition : needUpdateColumnDefinitions) {
            Object newValue = ReflectUtil.getFieldValue(this.model, columnDefinition.getField());

            columnDefinition.checkValue(newValue);

            sqlBuilder.append(columnDefinition.getWrapColumnName()).append("=");

            if (columnDefinition.isUseCurrentTimeWhenUpdate() && Util.isTimeType(columnDefinition.getFieldType())) {
                newValue = Util.createCurrentTimeByTimeType(columnDefinition.getFieldType());
                ReflectUtil.setFieldValue(this.model, columnDefinition.getField(), newValue);
            }

            if (null == newValue) {
                sqlBuilder.append("null,");
            } else {
                sqlBuilder.append("?,");
                addParamByColumnDefinition(columnDefinition, newValue);
            }
        }
        // 去掉最后面的 逗号
        sqlBuilder.setLength(sqlBuilder.length() - 1);
    }

    /**
     * 添加要进行更新的java属性名称
     */
    public UpdateBuilder addUpdateModelFieldNames(String... needUpdateModelFieldNames) {
        if (null != needUpdateModelFieldNames && needUpdateModelFieldNames.length > 0) {
            for (String modelFieldName : needUpdateModelFieldNames) {
                if (Util.isNotBlank(modelFieldName)) {
                    ColumnDefinition columnDefinition = getColumnDefinitionByModelFieldName(modelFieldName);
                    if (null != columnDefinition) {
                        this.customNeedUpdateColumnDefinitionSet.add(columnDefinition);
                    }
                }
            }
        }
        return this;
    }

    public UpdateBuilder removeUpdateModelFieldNames(String... needUpdateModelFieldNames) {
        if (null != needUpdateModelFieldNames && needUpdateModelFieldNames.length > 0) {
            for (String modelFieldName : needUpdateModelFieldNames) {
                if (Util.isNotBlank(modelFieldName)) {
                    ColumnDefinition columnDefinition = getColumnDefinitionByModelFieldName(modelFieldName);
                    if (null != columnDefinition) {
                        this.customNeedUpdateColumnDefinitionSet.remove(columnDefinition);
                    }
                }
            }
        }
        return this;
    }

    @Override
    protected UpdateBuilder self() {
        return this;
    }
}
